# _*_coding:utf-8_*_
import threading
import sys
import os
import time
from . import EE
from . import task
from ATS.globaldata import gdata
from ATS import util
from ATS import linearize4SemiCon
from ATS.errHandler import errHandler
from utils.log_util import loggings
import traceback

workpath = os.path.dirname(os.path.realpath(sys.argv[0]))
sys.path.insert(0, os.path.dirname(workpath))

WAIT_EE_COMPLETE = 0.001
WAIT_GUI_UNPAUSE = 0.005

NO_ERR = 0
EEM = None


def get_EEM():
    """
    Only 1 EEManager instance is existed in the ATS system.
    Call get_EEM to get the single instance.
    """
    global EEM
    if EEM is None:
        EEM = EEManager()
    return EEM


class EEManager:

    def __init__(self):
        self.running = False
        self.pauseFlag = False  # init as not pause
        self.stopFlag = False  # init as run
        self.EES = []  # Execute Engines
        for i in range(util.constant.EE_THREAD_NO):
            engEvent = threading.Event()  # 创建一个事件管理标志,默认为False
            engEvent.clear()  # 将event的标志设置为False
            engLock = threading.Lock()
            # 获取线程锁,阻塞调用线程 lock.release()解锁
            engLock.acquire()  # let EE stop first
            eeObj = EE.EE('ExecEngine%d' % (i + 1), engEvent, engLock)
            self.EES.append(eeObj)
            eeObj.start()

    def set_pause(self, flag):
        if self.running:
            self.pauseFlag = flag

    def wait_pause_release(self):
        while self.pauseFlag:
            time.sleep(WAIT_GUI_UNPAUSE)

    def get_running_flag(self):
        return self.running

    def on_stop(self):
        if self.running:
            self.stopFlag = True
            gdd = gdata.getGDD()
            gdd.update({'ATS_Stop_Flag': True})
            # execute user abort uap
            if 'User_Abort_UAP' in gdd and gdd['User_Abort_UAP'] is not None:
                uaf = os.path.join(util.constant.UAP_PATH, gdd['User_Abort_UAP'])
                if os.path.exists(uaf):
                    try:
                        exec(uaf)
                    except:
                        err_code = errHandler.EXEC_UAP_ERR
                        err_msg = traceback.format_exc()
                        errHandler.handle_err("EE Manage execute UAP Abort test", err_code, err_msg)

    def wait_evt(self, ee_obj):
        while not (ee_obj.engEvent.isSet()):
            time.sleep(WAIT_EE_COMPLETE)

    def exec_task(self, t, loopID=None):
        """
        Execute all kinds of task.
        loopID is supported for Loop_Task.
        loopID: "loopUnitStr_%d"%loopNum
        """
        if t.type == task.TEST_TASK or \
                t.type == task.UAP_TASK or \
                t.type == task.PRB_TASK:
            # for simple task execution
            if t.type == task.TEST_TASK and \
                    gdata.getGDD().get('Exit_Dev', False):
                return NO_ERR
            elif t.type == task.TEST_TASK:
                Cur_Dev = gdata.get_loc_info(t.get_location_ind())[4]
                gdata.updateGDD({'Cur_Loop_Num': loopID, 'Cur_Dev': Cur_Dev})

            if t.type == task.UAP_TASK and t.uap_level == task.TEST_LEVEL and \
                    gdata.getGDD().get('Exit_Dev', False):
                return NO_ERR
            if loopID and t.type == task.TEST_TASK:
                t.loopId = loopID
                # myLog loopID to global data
                gdata.updateGDD({'Cur_Loop_Num': loopID})
            else:
                t.loopId = None
            # 在引擎中添加任务
            self.EES[0].set_tasks([t], loopID)
            self.wait_evt(self.EES[0])
            return self.EES[0].err_code

        elif t.type == task.LOOP_TASK:
            # for Repeat Run Task
            lp = 0
            while True:
                lp = lp + 1
                if 0 < t.loopNum < lp:
                    # loop times reached
                    break

                if self.stopFlag or gdata.getGDD().get('Test_Abort', True):
                    # user abort test
                    for ee in self.EES:
                        ee.stop_task()
                    break

                for a_task in t.loopTasks:
                    if self.stopFlag or gdata.getGDD().get('Test_Abort', True):
                        for ee in self.EES:
                            ee.stop_task()
                        break
                    err = self.exec_task(a_task, loopID=lp)
                    if err != NO_ERR:
                        return err
        elif t.type == task.PARALLEL_TASK:
            need_engines = len(t.list4devtasks)
            if need_engines > util.constant.EE_THREAD_NO:
                errHandler.handle_err('EEM', errHandler.INVAL_EE_CONFIG,
                                      "Please configure Execution Engine correctly!",
                                      level=logger.FATAL)
                return errHandler.INVAL_EE_CONFIG
            for ind in range(need_engines):
                # send device tasks to the Execution Engine
                self.EES[ind].set_tasks(t.list4devtasks[ind])
            for ind in range(len(t.list4devtasks)):
                self.wait_evt(self.EES[ind])
            all_fail = True
            for ind in range(need_engines):
                if self.EES[ind].err_code == NO_ERR:
                    all_fail = False
            if all_fail:
                errHandler.handle_err('EEM', errHandler.ALL_EES_FAILED,
                                      "All execution engine failed for %s testing!" % t.devs,
                                      level=logger.FATAL)
                return errHandler.ALL_EES_FAILED
        return NO_ERR

    def run_MTL(self):
        # reset EEM status
        self.running = True
        self.stopFlag = False
        self.pauseFlag = False
        b = time.time()
        MTL = linearize4SemiCon.linearizeTestPlan.getMTL()
        if len(self.EES) == 0:
            self.running = False
            gdata.updateGDD({"Set_Manual_Run": False})
            return errHandler.INVAL_EE_CONFIG
        ind = -1
        while True:
            if self.stopFlag or gdata.getGDD().get('Test_Abort', True):
                for ee in self.EES:
                    ee.stop_task()
                self.stopFlag = False
                break
            if self.pauseFlag:
                self.wait_pause_release()
            ind += 1
            if ind >= len(MTL):
                break
            t = MTL[ind]
            err = self.exec_task(t)
            if err != NO_ERR:
                if not errHandler.can_ignore(err):
                    self.running = False
                    gdata.updateGDD({"Set_Manual_Run": False})
                    return err
        loggings.debug(f'Execution Engine Manager {time.time() - b}')
        self.running = False
        gdata.updateGDD({"Set_Manual_Run": False})
        return NO_ERR
